package com.itstyle.seckill.service.impl;

import com.itstyle.seckill.common.dynamicquery.DynamicQuery;
import com.itstyle.seckill.common.entity.Result;
import com.itstyle.seckill.common.entity.SuccessKilled;
import com.itstyle.seckill.common.enums.SeckillStatEnum;
import com.itstyle.seckill.distributedlock.redis.RedissLockUtil;
import com.itstyle.seckill.service.SeckillVsService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.connection.RedisConnection;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.sql.Timestamp;
import java.util.Date;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicLong;

@Service
public class SeckillVsServiceImpl implements SeckillVsService {
    AtomicLong num = new AtomicLong(200);  //库存
    AtomicLong uid = new AtomicLong(0);    //代替用户id
    String REDIS_STOCK_HEAD = "ms";

    @Autowired
    private DynamicQuery dynamicQuery;
    @Autowired
    private RedisTemplate redisTemplate;

    @Override
    public Result atomic(long seckillId) {
        Long stock = num.get();	//volatile没有原子性

        if(stock>0) {
            stock = num.getAndDecrement();//
            if (stock > 0) {
                saveAndUpdate(seckillId);
                System.out.println(uid + "success");
            } else {
                System.out.println(uid + "系统没库存" + stock);
            }
        }
        return Result.ok();
    }

    @Override
    public Result redisBuffDecr(long seckillId) {
        String key = REDIS_STOCK_HEAD + seckillId;
        long stock = Long.valueOf(redisTemplate.opsForValue().get(key).toString());
        if(stock<=0){
            System.out.println("系统没库存"+stock);
            return Result.error();
        }
        RedisConnection conn = redisTemplate.getConnectionFactory().getConnection();
        stock = conn.decr(redisTemplate.getKeySerializer().serialize(key))+1;
        conn.close();
        if(stock>0){
            saveAndUpdate(seckillId);
            System.out.println(uid + "success");
        }else {
            System.out.println(uid + "系统没库存"+stock);
        }
        return Result.ok();
    }

    @Override
    public Result redisBuffLock(long seckillId) {
        Long stock = (Long) redisTemplate.opsForValue().get(REDIS_STOCK_HEAD + seckillId);
        if(stock>0){//这里存在原子性问题，后面加锁后还要判断
            boolean res = false;
            try {
                res = RedissLockUtil.tryLock(seckillId+"", TimeUnit.MILLISECONDS, 2000, 20000);
                if (res) {
                    stock = (Long) redisTemplate.opsForValue().get(REDIS_STOCK_HEAD + seckillId);
                    if(stock>0) {
                        redisTemplate.opsForValue().set(REDIS_STOCK_HEAD + seckillId, stock - 1);
                        saveAndUpdate(seckillId);
                        System.out.println(uid + "success");
                    }else {
                        System.out.println(uid + "没库存");
                    }
                } else {
                    System.out.println("没获取锁");
                }
            }catch (Exception e) {
                e.printStackTrace();
            } finally{
                if(res){//释放锁
                    RedissLockUtil.unlock(seckillId+"");
                }
            }
        }else {
            System.out.println("没库存");
        }
        return Result.ok();
    }

    @Override
    public Result init(long seckillId) {
        num = new AtomicLong(200);
        redisTemplate.opsForValue().set(REDIS_STOCK_HEAD + seckillId, 200L);
        String nativeSql = "DELETE FROM  success_killed WHERE seckill_id=?";
        dynamicQuery.nativeExecuteUpdate(nativeSql, new Object[]{seckillId});
        nativeSql = "UPDATE seckill SET number =200 WHERE seckill_id=?";
        dynamicQuery.nativeExecuteUpdate(nativeSql, new Object[]{seckillId});
        return Result.ok();
    }

    @Override
    public Result atomicRedisBuffDecr(long seckillId) {
        long stock = num.get();	//volatile没有原子性

        if(stock>0) {
            stock = num.getAndDecrement();//
            if (stock > 0) {

                String key = REDIS_STOCK_HEAD + seckillId;
                stock = Long.valueOf(redisTemplate.opsForValue().get(key).toString());
                if(stock<=0){
                    System.out.println("系统没库存"+stock);
                    return Result.error();
                }
                RedisConnection conn = redisTemplate.getConnectionFactory().getConnection();
                stock = conn.decr(redisTemplate.getKeySerializer().serialize(key))+1;
                conn.close();
                if(stock>0){
                    saveAndUpdate(seckillId);
                    System.out.println(uid + "success");
                }else {
                    System.out.println(uid + "系统没库存"+stock);
                }
                return Result.ok();

            } else {
                System.out.println(uid + "系统没库存" + stock);
            }
        }
        return Result.ok();
    }

    @Override
    public Result startSeckilRedisLock(long seckillId, int i) {
        boolean res=false;
        try {
            res = RedissLockUtil.tryLock(seckillId+"", TimeUnit.SECONDS, 3, 20);
            if(res){
                String nativeSql = "SELECT number FROM seckill WHERE seckill_id=?";
                Object object =  dynamicQuery.nativeQueryObject(nativeSql, new Object[]{seckillId});
                Long number =  ((Number) object).longValue();
                if(number>0){
                    SuccessKilled killed = new SuccessKilled();
                    killed.setSeckillId(seckillId);
                    killed.setUserId(uid.getAndIncrement());
                    killed.setState((short)0);
                    killed.setCreateTime(new Timestamp(new Date().getTime()));
                    dynamicQuery.save(killed);
                    nativeSql = "UPDATE seckill  SET number=number-1 WHERE seckill_id=? AND number>0";
                    dynamicQuery.nativeExecuteUpdate(nativeSql, new Object[]{seckillId});
                }else{
                    return Result.error(SeckillStatEnum.END);
                }
            }else{
                return Result.error(SeckillStatEnum.MUCH);
            }
        } catch (Exception e) {
            e.printStackTrace();
        } finally{
            if(res){//释放锁
                RedissLockUtil.unlock(seckillId+"");
            }
        }
        return Result.ok(SeckillStatEnum.SUCCESS);
    }

    @Override
    @Transactional
    public Result startSeckilDBPCC_ONE(long seckillId, int i) {
        //单用户抢购一件商品或者多件都没有问题
        String nativeSql = "SELECT number FROM seckill WHERE seckill_id=? FOR UPDATE";
        Object object =  dynamicQuery.nativeQueryObject(nativeSql, new Object[]{seckillId});
        Long number =  ((Number) object).longValue();
        if(number>0){
            nativeSql = "UPDATE seckill  SET number=number-1 WHERE seckill_id=?";
            dynamicQuery.nativeExecuteUpdate(nativeSql, new Object[]{seckillId});
            SuccessKilled killed = new SuccessKilled();
            killed.setSeckillId(seckillId);
            killed.setUserId(uid.getAndIncrement());
            killed.setState((short)0);
            killed.setCreateTime(new Timestamp(new Date().getTime()));
            dynamicQuery.save(killed);
            return Result.ok(SeckillStatEnum.SUCCESS);
        }else{
            return Result.error(SeckillStatEnum.END);
        }
    }

    private void saveAndUpdate(long seckillId){
        SuccessKilled killed = new SuccessKilled();
        killed.setSeckillId(seckillId);
        killed.setUserId(uid.getAndIncrement());
        killed.setState((short) 0);
        killed.setCreateTime(new Timestamp(new Date().getTime()));
        dynamicQuery.save(killed);
        String nativeSql = "UPDATE seckill  SET number=number-1 WHERE seckill_id=?";
        dynamicQuery.nativeExecuteUpdate(nativeSql, new Object[]{seckillId});
    }

}
